Repository navigation
fix(core): 对齐 strict 与 recovery 的接受集,DA1 回复 VT102 - #272
Conversation
真实捕获 experiments/conpty/results/2026-10-27-native-viewport-resize/case-0/ transport.bin 在 strict 下第一个控制序列就失败,核心无法回放任何真实 ConPTY 传输。 按「记录型接受、不改变行为」的最小范围补三条: - ?1004 焦点上报:接受并忽略。核心不绘制焦点状态,宿主也没有可作用的对象 - ?9001 Win32 输入模式:接受并忽略,同上 - CSI 1t 窗口操作(XTWOL/XTMOD):接受并忽略。这是 ConPTY 在原生 resize 附近 发出的通知,按它改变几何会与宿主自身的尺寸管理冲突 有意不做的: - ESC[c(DA1)仍抛错。回复内容等于声明终端能力,核心实现的是很有限的子集, 回复哪一种都需要先定策略,本提交不预设 - ?2004 bracketed paste 与 ?1000 鼠标模式仍抛错,避免用「接受」制造已支持的假象 效果:上面那份捕获的解析从字节 0 推进到字节 4(即 ESC[c 处),前三项的接受 不再阻塞回放。
决定 DA1 策略:回复 ESC[?6c(VT102)。依据是候选实现 (vendor/alacritty_terminal/src/term/mod.rs:1214)对 CSI c 回同一条, 且 VT102 是核心实际能支撑的最小声明——光标定位、擦除、插删行/字符、 SGR、DECAWM、备用屏都在实现范围内,而更高版本号会过度声明。 同时澄清一处此前的误读:核心有两条解析路径。feed_strict(旧路径, replay 与测试在用)遇到 OSC 等序列直接抛错;recovery(产品路径)本就 会吞掉这些并计数。本提交让 strict 在 ?1004/?9001/CSI 1-3t/DA1 上与 recovery 对齐,而不是新增产品能力。 保留仍抛错的项(并有测试断言):?2004 bracketed paste、?1000 鼠标、 CSI 4t/11t 等查询、OSC(strict 路径尚未支持标题)。
ConPTY emits DA1 exactly once in each session's startup packet. On the product recovery path that now produces a pending reply, which poll() sends back through the same bounded input path as keystrokes. When a session is closed before its first poll (resize_lifecycle_boundaries mode 0), send() hits !running() and records a fatal 'Terminal input is unavailable' that the await helper surfaces. A reply only matters while the child is still attached, so skip it once the session is no longer running. This also removes the same latent race for DSR cursor-position replies.
The product parser runs in recovery mode, but the shared dispatch() added in #272 made recovery answer the fixed ConPTY startup packet's DA1 query with ESC[?6c too. The Qt entry drains that reply and sends it back through the bounded input path as if it were a keystroke at session startup, which races with the session lifecycle: when the child has exited or is closing, the input queue is already stopping and send() records "Terminal input queue is full or unavailable" (qt_cpp_shell PR-event failures). Answer DA1 only in strict mode (replay/tests), matching the #272 design table where strict is for replay/testing and recovery is the product path that stays silent.
诊断结论:不是 DA1 回路,而是 DA1 回复与快速关闭会话的竞态已定位根因并提交修复 证据链(实测)
CI 实测(两个失败运行的日志完全一致)
对「查询→回复→再查询」回路假设的判定:证伪
修复
red→green:
(任务描述中的 windeployqt 硬杀是另一次资源类失败,非本代码路径;未在此处理。) |
根因:recovery(产品路径)不该回复 DA1结论: 实测(固定 ConPTY 1.25 + 逐字节探针,区分实测/推断):
推断(CI 日志 41288373162): 这条唯一的启动期回复经 修复
CI
commit: |
…modes Bring in main's DECOM origin-mode work (#301) so the DA1 fix can be CI-tested against current main. Resolves the private-CSI block and test registration conflicts; both additions are kept.
|
跟进:CI 已转绿。 推送修复后 main 又前进了(
核心合并后本地验证: |
问题与最终行为
main的feed_strict路径(replay与测试在用)对真实 ConPTY 流里的若干序列直接抛错,而产品的recovery路径本就吞掉它们并计数。两者对同一份字节给出不同结论,测试路径因此无法回放真实捕获。本 PR 让 strict 在这几类序列上与 recovery 对齐。
ESC[?1004h/l焦点上报ESC[?9001h/lWin32 输入模式ESC[1/2/3t窗口命令ESC[cDA1ESC[?6cESC[?2004hbracketed pasteESC[?1000h鼠标ESC[4t/ESC[11t查询为什么 DA1 回
ESC[?6cESC[?6c(vendor/alacritty_terminal/src/term/mod.rs:1214),差分对照因此一致pending_replies通道,与CSI 6n的 DSR 回复同一机制为什么有四项仍抛错
接受
?2004/?1000会让应用以为终端支持括号粘贴或鼠标上报,属于用"不报错"制造虚假能力;CSI 4t/11t是需要回复的查询,静默吞掉会让应用一直等;OSC 标题在 strict 路径尚未支持。这些保持显式失败,并在测试里断言仍然抛错。验证
新增
experiments/core/private_mode_test.cpp,注册为 CTestprivate_mode_core:用例覆盖:
?1004h/l、?9001h/l前后屏幕 JSON 与纯文本输入完全一致(接受但无副作用)ESC[1/2/3t同样无副作用ESC[c只产生一条ESC[?6c回复(take_replies()断言)?2004h、?1000h、ESC[4t、ESC[11t仍然抛错真实捕获的推进(
2026-10-27-native-viewport-resize/case-0/transport.bin,104 字节):OSC 是 strict 路径的下一个缺口,本 PR 不处理。
性能影响(如适用)
不适用。分派分支增加,无运行期成本。
来源与限制
?9001等仍是丢弃而非支持,Win32 输入模式与括号粘贴的实际语义仍未实现